﻿using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Diagnostics;
using ToolMonitoring.WebApi.Entities;
using ToolMonitoring.WebApi.Services;
using Newtonsoft.Json;
using System.Reflection;
using ToolMonitoring.WebApi.Extensions;

namespace ToolMonitoring.WebApi.Filters
{
	public class AuditLogActionFilter: IAsyncActionFilter
	{
		private static readonly int FieldsLength250 = 250;

		/// <summary>
		/// 审计日志服务对象
		/// </summary>
		private readonly AuditLogService _auditLogService;
		
		/// <summary>
		/// 日志记录
		/// </summary>
		private readonly ILogger<AuditLogActionFilter> _logger;

		public AuditLogActionFilter(
			AuditLogService auditLogService,
			ILogger<AuditLogActionFilter> logger
			)
		{
			_logger = logger;
			_auditLogService = auditLogService;
		}

		public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
		{
			// 判断是否写日志
			if (!ShouldSaveAudit(context))
			{
				await next();
				return;
			}
			//接口Type
			var type = (context.ActionDescriptor as ControllerActionDescriptor).ControllerTypeInfo.AsType();
			//方法信息
			var method = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo;
			//方法参数
			var arguments = context.ActionArguments;
			//开始计时
			var stopwatch = Stopwatch.StartNew();
			var auditInfo = new AuditLog
			{
				// UserInfo = _Session?.Id,
				ServiceName = type != null ? type.FullName.TruncateWithPostfix(FieldsLength250) : "",
				MethodName = method.Name.TruncateWithPostfix(FieldsLength250),
				////请求参数转Json
				Parameters = JsonConvert.SerializeObject(arguments),
				ExecutionTime = DateTime.Now,
				BrowserInfo = context.HttpContext.Request.Headers["User-Agent"].ToString().TruncateWithPostfix(FieldsLength250),
				ClientIpAddress = context.HttpContext.Connection.RemoteIpAddress.ToString().TruncateWithPostfix(FieldsLength250),
				//ClientName = _clientInfoProvider.ComputerName.TruncateWithPostfix(EntityDefault.FieldsLength100),
				Id = Guid.NewGuid()
			};

			ActionExecutedContext result = null;
			try
			{
				result = await next();
				if (result.Exception != null && !result.ExceptionHandled)
				{
					auditInfo.Exception = result.Exception.StackTrace.TruncateWithPostfix(AuditLog.MaxExceptionLength);
					auditInfo.ExceptionMessage = result.Exception.Message.TruncateWithPostfix(AuditLog.MaxExceptionMessageLength);

				}
			}
			catch (Exception ex)
			{
				auditInfo.Exception = result.Exception.StackTrace.TruncateWithPostfix(AuditLog.MaxExceptionLength);
				auditInfo.ExceptionMessage = result.Exception.Message.TruncateWithPostfix(AuditLog.MaxExceptionMessageLength);
				throw;
			}
			finally
			{
				stopwatch.Stop();
				auditInfo.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds);

				if (result != null)
				{
					switch (result.Result)
					{
						case ObjectResult objectResult:
							auditInfo.ReturnValue = JsonConvert.SerializeObject(objectResult.Value);
							break;

						case JsonResult jsonResult:
							auditInfo.ReturnValue = JsonConvert.SerializeObject(jsonResult.Value);
							break;

						case ContentResult contentResult:
							auditInfo.ReturnValue = contentResult.Content;
							break;
					}
				}
				Console.WriteLine(auditInfo.ToString());
				//保存审计日志
				await _auditLogService.Add(auditInfo);
			}
		}

		/// <summary>
		/// 是否需要记录审计
		/// </summary>
		/// <param name="context"></param>
		/// <returns></returns>
		private bool ShouldSaveAudit(ActionExecutingContext context)
		{
			if (!(context.ActionDescriptor is ControllerActionDescriptor))
				return false;
			var methodInfo = (context.ActionDescriptor as ControllerActionDescriptor).MethodInfo;

			if (methodInfo == null)
			{
				return false;
			}

			if (!methodInfo.IsPublic)
			{
				return false;
			}

			if (methodInfo.GetCustomAttribute<AuditedAttribute>() != null)
			{
				return true;
			}

			if (methodInfo.GetCustomAttribute<DisableAuditingAttribute>() != null)
			{
				return false;
			}

			var classType = methodInfo.DeclaringType;
			if (classType != null)
			{
				if (classType.GetTypeInfo().GetCustomAttribute<AuditedAttribute>() != null)
				{
					return true;
				}


				if (classType.GetTypeInfo().GetCustomAttribute<DisableAuditingAttribute>() != null)
				{
					return false;
				}

				if (classType.GetTypeInfo().GetCustomAttribute<ApiControllerAttribute>() != null)
				{
					return true;
				}
			}
			return false;
		}

	}
}
